package service

import (
	"errors"
	"exercise/application/yiran/app"
	"exercise/application/yiran/app/factory/FileUpload"
	"exercise/application/yiran/app/libs"
	"exercise/application/yiran/app/model"
	"exercise/application/yiran/app/request"
	"fmt"
	"github.com/gin-gonic/gin"
	"io"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"strings"
	"time"
)

type uploaderService struct {
}

func NewUploaderService() *uploaderService {
	return &uploaderService{}
}

// UploadFile 文件上传
func (u *uploaderService) UploadFile(ctx *gin.Context, request *request.UploadFileRequest) (id int64, err error) {
	typeList := u.fileType()
	_, ok := typeList[request.UploadType]
	if !ok {
		return id, errors.New("文件类型异常")
	}

	file := request.File
	//获取后缀
	ext := path.Ext(file.Filename)
	//将后缀转换成小写
	ext = strings.ToLower(ext)
	//文件校验
	folder, extAll := u.GetFolder(request.UploadType)
	_, ok = extAll[ext]
	if !ok {
		return id, errors.New("请上传符合要求的文件")
	}
	fileName, err := FileUpload.NewUploaderFactory(folder, "", ext).UploadFile(file)

	if err != nil {
		return id, err
	}

	//将文件保存到文件中心
	fileCenter := new(model.FileCenter)
	fileCenter.FileType = ext[1:]
	fileCenter.SavePath = fileName
	fileCenter.OriginFilename = file.Filename
	fileCenter.Filename = strings.Replace(fileName, folder+"/", "", -1)
	fileCenter.CreateTime = libs.CurrentTime()
	model.NewDb("").SaveRecord(fileCenter)
	return fileCenter.Id, err
}

// GetFolder 获取文件存储地址
func (u *uploaderService) GetFolder(uploadType string) (folder string, extAll map[string]bool) {
	extAll = make(map[string]bool)
	switch uploadType {
	case "excel":
		folder = "excel"
		extAll = map[string]bool{
			".xlsx": true,
			".xls":  true,
		}
	case "zip":
		folder = "zip"
		extAll = map[string]bool{
			".zip": true,
		}

	default:
		folder = "images"
		extAll = map[string]bool{
			".jpg":  true,
			".jpeg": true,
			".png":  true,
			".gif":  true,
			".bmp":  true,
		}
	}
	return
}

// SliceUpload 切片上传
func (u *uploaderService) SliceUpload(request *request.SliceUploadFileRequest) (bool, error) {
	file := request.File
	//获取后缀
	ext := path.Ext(file.Filename)
	//将后缀转换成小写
	ext = strings.ToLower(ext)
	//存放切片的文件
	sliceFolderTmp := strings.Split(filepath.Base(request.Filename), ".")
	sliceFolder := sliceFolderTmp[0]
	//创建目录
	path := filepath.Join(app.YiRanSlicePath, sliceFolder)
	libs.CreateDetect(path)
	//文件全名
	newFilename := request.Filename + "_" + fmt.Sprintf("%d", request.ChunkIndex)
	fullName := filepath.Join(path, newFilename)
	fileHandle, err := file.Open()
	if err != nil {
		return false, errors.New("读取文件失败")
	}
	//创建文件
	out, err := os.Create(fullName)
	if err != nil {
		return false, errors.New("创建文件失败")
	}
	defer func() {
		_ = fileHandle.Close()
		_ = out.Close()
	}()

	//file图片拷贝到新创建的文件中
	_, copyErr := io.Copy(out, fileHandle) // 传输（拷贝）文件
	if copyErr != nil {
		return false, errors.New("保存失败")
	}
	return true, nil
}

// MergeSliceChunks 合并切片
func (u *uploaderService) MergeSliceChunks(request *request.SliceUploadFileRequest) (int64, error) {
	var totalSize int
	var fileId int64
	//将后缀转换成小写
	ext := path.Ext(request.Filename)
	ext = strings.ToLower(ext)
	//存放切片临时文件夹
	sliceFolderTmp := strings.Split(filepath.Base(request.Filename), ".")
	sliceFolder := sliceFolderTmp[0]
	chunksPath := filepath.Join(app.YiRanSlicePath, sliceFolder)
	//保存的新文件名
	newFilename := time.Now().Format("20060102150405") + ext
	filePath := app.YiRanPublicDir + newFilename
	fileHandler, err := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY, 0666)
	if err != nil {
		return fileId, errors.New("创建文件失败")
	}
	defer fileHandler.Close()
	for i := 0; i < request.TotalChunks; i++ {
		chunkPath := filepath.Join(chunksPath, request.Filename+"_"+fmt.Sprintf("%d", i))
		if _, err := os.Stat(chunkPath); os.IsNotExist(err) {
			return fileId, errors.New("切片文件不存在")
		}
		chunkContent, err := ioutil.ReadFile(chunkPath)
		if err != nil {
			return fileId, errors.New("读取切片文件失败")
		}
		if _, err := fileHandler.Write(chunkContent); err != nil {
			return fileId, errors.New("写入切片文件失败")
		}
		totalSize += len(chunkContent)
	}

	return fileId, nil
}

// fileType 文件类型
func (u *uploaderService) fileType() map[string]bool {
	return map[string]bool{"images": true, "excel": true, "zip": true}
}
